package com.jhf.youke.base.domain.service;

import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.json.JSONUtil;
import com.jhf.youke.base.domain.converter.UserConverter;
import com.jhf.youke.base.domain.converter.UserRoleConverter;
import com.jhf.youke.base.domain.exception.UserException;
import com.jhf.youke.base.domain.gateway.CompanyRepository;
import com.jhf.youke.base.domain.gateway.PermissionsSetRepository;
import com.jhf.youke.base.domain.gateway.UserRepository;
import com.jhf.youke.base.domain.gateway.UserRoleRepository;
import com.jhf.youke.base.domain.model.Do.UserDo;
import com.jhf.youke.base.domain.model.dto.RegisterCustomerDto;
import com.jhf.youke.base.domain.model.dto.RegisterRegimentalCommanderDto;
import com.jhf.youke.base.domain.model.dto.VideoLoginDto;
import com.jhf.youke.base.domain.model.po.CompanyPo;
import com.jhf.youke.base.domain.model.po.PermissionsPo;
import com.jhf.youke.base.domain.model.po.UserPo;
import com.jhf.youke.base.domain.model.po.UserRolePo;
import com.jhf.youke.base.domain.model.vo.UserVo;
import com.jhf.youke.core.ddd.AbstractDomainService;
import com.jhf.youke.core.entity.PageQuery;
import com.jhf.youke.core.entity.Pagination;
import com.jhf.youke.core.utils.CacheUtils;
import com.jhf.youke.core.utils.Constant;
import com.jhf.youke.core.utils.DateUtils;
import com.jhf.youke.core.utils.StringUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author rhj
 * **/
@Service
public class UserService extends AbstractDomainService<UserRepository, UserDo, UserVo> {


    @Resource
    UserConverter userConverter;

    @Resource
    UserRoleRepository userRoleRepository;

    @Resource
    UserRoleConverter userRoleConverter;

    @Resource
    PermissionsSetRepository permissionsSetRepository;

    @Resource
    CompanyRepository companyRepository;


    @Override
    public boolean update(UserDo entity) {
        UserPo userPo = userConverter.do2Po(entity);
        userPo.preUpdate();
        return repository.update(userPo);
    }

    @Override
    public boolean updateBatch(List<UserDo> doList) {
        List<UserPo> poList = userConverter.do2PoList(doList);
        return repository.updateBatch(poList);
    }

    @Override
    public boolean delete(UserDo entity) {
        UserPo userPo = userConverter.do2Po(entity);
        return repository.delete(userPo);
    }

    @Override
    public boolean deleteBatch(List<Long> idList) {
        return repository.deleteBatch(idList);
    }

    @Override
    public boolean insert(UserDo entity) {
        entity.initPassword();
        UserPo userPo = userConverter.do2Po(entity);
        userPo.preInsert();
        return repository.insert(userPo);
    }

    public UserPo save(UserDo entity) {
        entity.initPassword();
        UserPo userPo = userConverter.do2Po(entity);
        userPo.preInsert();
        repository.insert(userPo);
        return userPo;
    }

    @Override
    public boolean insertBatch(List<UserDo> doList) {
        doList.forEach(entity -> entity.initPassword());
        List<UserPo> poList = userConverter.do2PoList(doList);
        UserPo.getInsertListId(poList);
        return repository.insertBatch(poList);
    }

    @Override
    public Optional<UserVo> findById(Long id) {
        Optional<UserPo> userPo =  repository.findById(id);
        UserVo userVo = userConverter.po2Vo(userPo.orElse(new UserPo()));
        return Optional.ofNullable(userVo);
    }

    @Override
    public boolean remove(Long id) {
        return repository.remove(id);
    }

    @Override
    public boolean removeBatch(List<Long> idList) {
        return repository.removeBatch(idList);
    }

    @Override
    public List<UserVo> findAllMatching(UserDo entity) {
        UserPo userPo = userConverter.do2Po(entity);
        List<UserPo>userPoList =  repository.findAllMatching(userPo);
        return userConverter.po2VoList(userPoList);
    }


    @Override
    public Pagination<UserVo> selectPage(UserDo entity){
        if(entity.getEndTime() != null){
            entity.setEndTime(DateUtils.addDays(entity.getEndTime(), 1));
        }
        UserPo userPo = userConverter.do2Po(entity);
        PageQuery<UserPo> pageQuery = new PageQuery<>(userPo,entity.getCurrentPage(), entity.getPageSize(), entity.getQuerySort());
        Pagination<UserPo> pagination = repository.selectPage(pageQuery);
        return new Pagination<UserVo>(pagination.getPageNum(),pagination.getPageSize(),pagination.getTotalSize(),
                userConverter.po2VoList(pagination.getList()));
    }


    public boolean addRoles(UserDo userDo){
        return  userRoleRepository.insertBatch(userDo.getUserRoles());
    }

    /**
     * @Description: 根据解密后Json进行登录
     * @Param: [login]
     * @return: java.util.Map<java.lang.String,java.lang.Object>
     * @Author: RHJ
     * @Date: 2022/9/21
     */
    public Map<String,Object> loginByPwd(String login) {
        Map<String,Object> res = new HashMap<>(8);
        Map map = JSONUtil.toBean(login, Map.class);
        String loginName = StringUtils.chgNull(map.get("loginName"));
        String password = StringUtils.chgNull(map.get("password"));

        UserPo userPo = repository.getByLoginName(loginName);
        if(userPo == null){
            throw new UserException("账号或密码错误");
        }
        UserDo userDo = userConverter.po2Do(userPo);
        userDo.checkPassword(password);
        userDo.checkStatus();
        if(userPo != null){
            res = userSaveToken(userDo);
        }
        return res;
    }

    public String getCompanyIds(Long companyId){
        String key = "company_ids_" +companyId;
        String companyIds = CacheUtils.get(key);
        if(StringUtils.isNotBlank(companyIds)){
            return companyIds;
        }else {
            companyIds = companyRepository.getCompanyIds(companyId);
            CacheUtils.set(key, companyIds, 20 * 60 * 60);
            return companyIds;
        }
    }

    private void initAuth(UserDo userDo) {
        // 添加用户权限
        List<PermissionsPo> permissionsPos = permissionsSetRepository.getListByUser(userDo.getId());
        Set<String> permissionsMap = permissionsPos.stream()
                .filter(bean -> bean.getId() != null)
                .map(permissionsPo ->permissionsPo.getCode())
                .collect(Collectors.toSet());
        userDo.setPermissionsMap(permissionsMap);
    }


    /**
     * 登录用户修改自己的信息
     * @param userId
     * @param userDo
     */
    public Boolean updateInfoByUserId(Long userId, UserDo userDo) {
        UserPo po = repository.findById(userId).get();
        po.setName(userDo.getName());
        po.setPhone(userDo.getPhone());
        po.setSex(userDo.getSex());
        po.setHeadImage(userDo.getHeadImage());
        po.preUpdate();
        return  repository.update(po);
    }

    /**
     * 登录用户修改自己的密码
     * @param userId
     * @param userDo
     * @return
     */
    public Boolean updatePasswordByUserId(Long userId, UserDo userDo) {
        UserPo userPo = repository.findById(userDo.getId()).get();
        userDo.setSalt(userPo.getSalt());
        userDo.setPassword(userPo.getPassword());
        // 检查原密码是否正确
        userDo.checkPassword(userDo.getOldPassword())
                // 检查两次密码是否
                .checkTwoPassword(userDo.getNewPassword1(),userDo.getNewPassword2())
                .updatePassword(userDo.getNewPassword1());
        UserPo po = userConverter.do2Po(userDo);
        return  repository.update(po);
    }

    public Boolean logout(String token) {
        CacheUtils.del(token);
        return true;
    }

    /**
     * 重置用户密码
     * @param userDo
     * @return
     */
    public Boolean resetPassword(UserDo userDo) {
        userDo.requireNonNull(this, userDo.getPassword(),"密码不能为空");
        UserPo po = repository.findById(userDo.getId()).get();
        po.setPassword(DigestUtil.md5Hex(userDo.getPassword() + po.getSalt()));
        po.preUpdate();
        return  repository.update(po);
    }

    public Boolean updateStatus(UserDo userDo) {
        userDo.requireNonNull(this, userDo.getStatus(), "状态不能为空");
        UserPo po = userConverter.do2Po(userDo);
        return  repository.update(po);
    }

    public Map<String, Object> loginByMobile(String login) {
        Map<String,Object> res = new HashMap<>(8);
        Map map = JSONUtil.toBean(login, Map.class);
        String openId = StringUtils.chgNull(map.get("openId"));

        UserPo userPo = repository.getByMobile(openId);
        if(userPo == null){
            res.put("code", Constant.CODE_PASSWORD_ERROR);
            return res;
        }

        UserDo userDo = userConverter.po2Do(userPo);

        userDo.checkStatus();

        if(userPo != null){
            res = userSaveToken(userDo);
        }

        res.put("code", Constant.CODE_NORMAL);
        return res;
    }


    public UserDo registerRegimentalCommander(RegisterRegimentalCommanderDto dto){
        // 邀请人id，团购名称[暂未]，用户名，姓名，手机号码，密码，小程序openId
        UserPo userPo = getCheckUser(dto.getReferrer(), dto.getLoginName(), dto.getOpenId());
        UserDo userDo = new UserDo(userPo, dto);
        userDo = userConverter.po2Do(save(userDo));
        //添加默认角色
        UserRolePo userRolePo = new UserRolePo();
        userRolePo.setRoleId(Constant.REGIMENT_USER_DEFAULT_ROLE);
        userRolePo.setUserId(userPo.getId());
        userRolePo.preInsert();
        userRoleRepository.insert(userRolePo);
        return userDo;
    }

    public UserDo registerCustomer(RegisterCustomerDto dto) {
        UserPo userPo = getCheckUser(dto.getSaleMan(), dto.getLoginName(), dto.getOpenId());
        UserDo userDo = new UserDo(userPo, dto);
        userDo = userConverter.po2Do(save(userDo));
        //添加默认角色
        UserRolePo userRolePo = new UserRolePo();
        userRolePo.setRoleId(Constant.CUSTOMER_USER_DEFAULT_ROLE);
        userRolePo.setUserId(userPo.getId());
        userRolePo.preInsert();
        userRoleRepository.insert(userRolePo);
        return userDo;
    }

    private UserPo getCheckUser(Long id, String loginName, String openId) {
        Optional<UserPo> userOption = repository.findById(id);
        assert !userOption.isEmpty() : "推荐人不能为空";
        UserPo checkLoginName = repository.getByLoginName(loginName);
        assert checkLoginName == null : "名称已被使用";
        UserPo checkOpenId = repository.getByMobile(openId);
        assert checkOpenId == null : "用户已注册";
        return userOption.get();
    }

    /**
     * 视频小程序登录
     * @param dto
     * @return
     */
    public Map<String, Object> videoLogin(VideoLoginDto dto) {
        UserPo prarm = new UserPo();
        prarm.setOpenId(dto.getOpenid());
        prarm.setType(6);
        List<UserPo> list = repository.findAllMatching(prarm);
        if(list.size() > 0){
            UserPo userPo = list.get(0);
            //已注册
            UserDo userDo = userConverter.po2Do(userPo);
            userDo.checkStatus();
            return userSaveToken(userDo);
        }else {
            //未注册
            return null;
        }
    }

    /**
     * 视频小程序注册
     * @param dto
     * @return
     */
    public Map<String, Object> videoRegister(VideoLoginDto dto) {
        Optional<CompanyPo> companyPo = companyRepository.findById(dto.getCompanyId());
        dto.setCompanyName(companyPo.get().getName());
        dto.setRootId(companyPo.get().getRootId());
        UserDo userDo = new UserDo(dto);

        UserPo userPo = userConverter.do2Po(userDo);
        userPo.preInsert();
        repository.insert(userPo);

        //添加默认角色
        UserRolePo userRolePo = new UserRolePo();
        userRolePo.setRoleId(Constant.VIDEO_USER_DEFAULT_ROLE);
        userRolePo.setUserId(userPo.getId());
        userRolePo.preInsert();
        userRoleRepository.insert(userRolePo);

        Map<String, Object> res = new HashMap<>(2);
        String token = UUID.randomUUID().toString();

        // 添加用户权限
        List<PermissionsPo> permissionsPos = permissionsSetRepository.getListByRoleId(userRolePo.getRoleId());
        Set<String> permissionsMap = permissionsPos.stream()
                .filter(bean -> bean.getId() != null)
                .map(permissionsPo ->permissionsPo.getCode())
                .collect(Collectors.toSet());
        userDo.setPermissionsMap(permissionsMap);

        userDo.setCompanyIds(getCompanyIds(userDo.getCompanyId()));
        res.put("token", token);
        res.put("user", userDo);
        CacheUtils.set(token, JSONUtil.toJsonStr(userDo), 60 * 60);
        return res;
    }

    private Map<String, Object> userSaveToken(UserDo userDo) {
        Map<String, Object> res = new HashMap<>(2);
        String token = UUID.randomUUID().toString();
        initAuth(userDo);
        userDo.setCompanyIds(getCompanyIds(userDo.getCompanyId()));
        res.put("token", token);
        res.put("user", userDo);
        CacheUtils.set(token, JSONUtil.toJsonStr(userDo), 60 * 60);
        return res;
    }

}
